/*
 * This file is part of the Sx Framework Library.
 * 
 * Copyright (C) 2014 University of Colorado Denver
 * <min.choi@ucdenver.edu> <shane.transue@ucdenver.edu>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 */
#ifndef SX_BEZIER_CONTROLLER_H
#define SX_BEZIER_CONTROLLER_H

#include <sxPlotController.h>
#include <sxBezier_Archetype.h>
#include <sxArrayList.h>

/* Sx library types. */
using Sx::Graphics::Archetypes::Objects::Bezier_Archetype;

namespace Sx {
namespace Interface {

/*
 *
 * [Renderer Implementations]
 * cameraRenderer - Renderer<Camera, StaticRepresentation>
 * plotRenderer - Renderer<PlotAxes, PlotAxes_Representation>
 * bezierRenderer - Renderer<BezierCurve, StaticRepresentation>
 */
class BezierEditController : public PlotController {
public:
	/* Creates a new default Bezier curve edit controller. */
	BezierEditController(Viewport* viewport,
		const std::shared_ptr<Canvas>& canvas,
		const std::shared_ptr<ScenePickingController>& pickingController,
		const std::shared_ptr<RendererImplementation>& cameraRenderer, 
		const std::shared_ptr<RendererImplementation>& plotRenderer,
		const std::shared_ptr<RendererImplementation>& bezierRenderer);

	BezierEditController(Viewport* viewport,
		const std::shared_ptr<Canvas>& canvas,
		const std::shared_ptr<ScenePickingController>& pickingController,
		const std::shared_ptr<RendererImplementation>& cameraRenderer, 
		const std::shared_ptr<RendererImplementation>& plotRenderer,
		const std::shared_ptr<RendererImplementation>& bezierRenderer,
		unsigned int nxTicks,
		unsigned int pxTicks,
		unsigned int nyTicks,
		unsigned int pyTicks,
		unsigned int nzTicks,
		unsigned int pzTicks,
		float spacing);

	/* 
	 * Creates a new Bezier curve edit controller where the default positions
	 * of the Bezier curves end points and control points are provided.
	 */
	BezierEditController(Viewport* viewport,
		const std::shared_ptr<Canvas>& canvas,
		const std::shared_ptr<ScenePickingController>& pickingController,
		const std::shared_ptr<RendererImplementation>& cameraRenderer, 
		const std::shared_ptr<RendererImplementation>& plotRenderer,
		const std::shared_ptr<RendererImplementation>& bezierRenderer,
		const Eigen::Vector3f& endPoint0,
		const Eigen::Vector3f& endPoint1,
		const Eigen::Vector3f& controlPoint0,
		const Eigen::Vector3f& controlPoint1);

	virtual ~BezierEditController();

	/* Viewport Interface Overriding */
	virtual bool initialize();
	virtual void render() const;

	/* Mouse interaction functions */
	virtual void mousePressEvent(int x, int y, MouseButton button, ModifierKey modifierKey);
	virtual void mouseReleaseEvent(int x, int y, MouseButton button, ModifierKey modifierKey);
	virtual void mouseClickEvent(int x, int y, MouseButton button, ModifierKey modifierKey);
	virtual void mouseMoveEvent(int x, int y, MouseButton button, ModifierKey modifierKey);
	virtual void mouseWheelEvent(int delta);

	/* Function that defines how the Bezier curve is changed during an edit. */
	virtual bool mouseEditBezierCurve(int x, int y, MouseButton button, ModifierKey modifierKey);

	/* 
	 * Ensures that the controlled points of the Bezier curve will be snapped
	 * to the grid when the mouse has released control. This is also the point
	 * where the number of points will be calculated if variablePointCurve is
	 * set to true.
	 */
	bool mouseSnapOnRelase();

	/* 
	 * If the curve editor is set to use a variable point curve, the curve will
	 * be reconstructed using a point for each grid snap location between the
	 * primary and secondary end points. This allows for a known number of 
	 * points to be used to construct the curve, namely: length / snapSpacing.
	 */
	void setVariablePointCurve(bool b);
	float getCurveLength(Math::Axis axis) const;
	unsigned int getCurvePointCount() const;
	bool getCurveApproximation(Util::ArrayList<Eigen::Vector3f>& approximationPoints);

	/* 
	 * Given the internal definition of the Bezier curve, data about the curve
	 * can be retrieved by axis. For example if the X-axis is provided then all
	 * of the x-coordinate data points from the curve will be loaded into the
	 * provided data array.
	 *
	 * Note: The data array will be erased when calling this function! It is
	 * intended to be an out parameter that is provided empty.
	 */
	bool populate(Util::ArrayList<float>& data, Math::Axis axis);

	void setSnap(bool enabled);
	void setSnapSpacing(float xSpacing, float ySpacing, float zSpacing);
	void setSnapSpacing(const Eigen::Vector3f& spacing);
	bool hasGridSnap() const;
	float getXSnapSpacing() const;
	float getYSnapSpacing() const;
	float getZSnapSpacing() const;
	Eigen::Vector3f getSnapSpacing() const;

	/* Returns the position of the end-point based on the grid-snap settings. */
	long getEndPointSnapPositionX(bool index) const;
	long getEndPointSnapPositionY(bool index) const;
	long getEndPointSnapPositionZ(bool index) const;

protected:
	std::shared_ptr<Bezier_Archetype> curve;

	/* 
	 * Tests for the intersection of the curve based on the provided x and y
	 * mouse coordinates. Intersection only reports true/false.
	 */
	bool intersectCurve(int x, int y);

	/*
	 * Tests for the intersection of the curve based on the provided x and y
	 * mouse coordinates, however it will modify the selection of the 
	 * intersected components in addition to returning true or false.
	 */
	bool pickCurve(int x, int y);

	/* 
	 * If this function is called, the number of points in the Bezier curve will 
	 * match that defined by the snap spacing. The curve will then be 
	 * reconstructed using that number of points.
	 */
	bool constructVariablePointCurve(Math::Axis axis);
	bool variablePointCurve;

	/* Selection distance (screen units) */
	unsigned int curvePointSelectionDistance;
	
	/* Define the spacing of the invisible snap grid. */
	float snapXSpacing, snapYSpacing, snapZSpacing;

	/* 
	 * If true the Bezier curves end and control points will be snapped to
	 * the invisible grid formed by snapXSpacing, snapYSpacing, and snapZSpacing
	 */
	bool gridSnap;

	/* True if the mouse is dragging the Bezier curve (editing the curve). */
	bool draggingCurve;
};

}

}

#endif
